using Engine;
using Engine.Graphics;
using Game;

namespace RuthlessConquest {
    public class BoidsWidget : Widget {
        public class Boid : Body {
            public int Faction;

            public Vector2 Velocity;

            public void Update() {
                float num = MathUtils.Clamp(Time.FrameDuration, 0f, 0.1f);
                TmpBodyList.Clear();
                Grid.QueryBodies(Position, 1000, TmpBodyList, 30);
                if (TmpBodyList.Count > 0) {
                    int num2 = 0;
                    int num3 = 0;
                    Vector2 zero = Vector2.Zero;
                    Vector2 zero2 = Vector2.Zero;
                    Vector2 zero3 = Vector2.Zero;
                    Vector2 zero4 = Vector2.Zero;
                    foreach (Body body in TmpBodyList) {
                        if (body is not Boid tmpBody) {
                            continue;
                        }
                        Vector2 vector = new(Position - tmpBody.Position);
                        float num4 = vector.Length();
                        Vector2 vector2 = vector / (num4 + 0.1f);
                        if (tmpBody.Faction == Faction) {
                            Velocity += 2000f * vector2 * MathUtils.Lerp(1f, 0f, MathUtils.Saturate((num4 - 100f) / 300f)) * num;
                            zero += new Vector2(tmpBody.Position);
                            zero2 += tmpBody.Velocity;
                            num2++;
                        }
                        else {
                            Velocity += 2000f * vector2 * MathUtils.Lerp(1f, 0f, MathUtils.Saturate((num4 - 100f) / 900f)) * num;
                            zero3 += new Vector2(tmpBody.Position);
                            zero4 += tmpBody.Velocity;
                            num3++;
                        }
                    }
                    if (num2 > 0) {
                        zero /= num2;
                        zero2 /= num2;
                        Vector2 vector3 = Vector2.Normalize(zero - new Vector2(Position));
                        Velocity += 200f * vector3 * num;
                        Velocity += 2f * zero2 * num;
                    }
                    if (num3 > 0) {
                        zero3 /= num3;
                        zero4 /= num3;
                        Vector2 vector4 = Vector2.Normalize(zero3 - new Vector2(Position));
                        Velocity -= 100f * vector4 * num;
                        Velocity -= 1f * zero4 * num;
                    }
                    float num5 = 500f;
                    if (Position.X < num5) {
                        Velocity += 10f * num * new Vector2(1f, 0f) * (num5 - Position.X);
                    }
                    if (Position.Y < num5) {
                        Velocity += 10f * num * new Vector2(0f, 1f) * (num5 - Position.Y);
                    }
                    if (Position.X > Grid.GridSize.X - num5) {
                        Velocity += 10f * num * new Vector2(-1f, 0f) * (Position.X - Grid.GridSize.X + num5);
                    }
                    if (Position.Y > Grid.GridSize.Y - num5) {
                        Velocity += 10f * num * new Vector2(0f, -1f) * (Position.Y - Grid.GridSize.Y + num5);
                    }
                }
                float num6 = Velocity.Length();
                float num7 = 250f;
                float num8 = 500f;
                if (num6 > num8) {
                    Velocity *= num8 / num6;
                }
                if (num6 < num7) {
                    Velocity *= num7 / num6;
                }
                Position = new Point2(Position.X + (int)MathUtils.Round(Velocity.X * num), Position.Y + (int)MathUtils.Round(Velocity.Y * num));
            }

            public void Draw(PrimitivesRenderer2D primitivesRenderer) {
                TexturedBatch2D texturedBatch2D = primitivesRenderer.TexturedBatch(GetTexture(Faction));
                Vector2 vector = new(Position);
                Vector2 vector2 = Vector2.Normalize(Velocity) * 150f;
                Vector2 vector3 = Vector2.Perpendicular(vector2);
                Vector2 vector4 = vector - vector3 + vector2;
                Vector2 p = vector + vector3 + vector2;
                Vector2 vector5 = vector + vector3 - vector2;
                Vector2 p2 = vector - vector3 - vector2;
                texturedBatch2D.QueueTriangle(
                    vector4,
                    p,
                    vector5,
                    0f,
                    new Vector2(0f, 0f),
                    new Vector2(1f, 0f),
                    new Vector2(1f, 1f),
                    Color
                );
                texturedBatch2D.QueueTriangle(
                    vector5,
                    p2,
                    vector4,
                    0f,
                    new Vector2(1f, 1f),
                    new Vector2(0f, 1f),
                    new Vector2(0f, 0f),
                    Color
                );
            }

            public static Texture2D GetTexture(int faction) {
                return faction switch {
                    0 => Textures.Gui.Boid1,
                    1 => Textures.Gui.Boid2,
                    2 => Textures.Gui.Boid3,
                    3 => Textures.Gui.Boid4,
                    4 => Textures.Gui.Boid5,
                    5 => Textures.Gui.Boid6,
                    _ => Textures.Gui.Boid1
                };
            }
        }

        public static BodyGrid Grid;

        public static DynamicArray<Boid> Boids;

        public static DynamicArray<Body> TmpBodyList = new();

        public static Random Random = new();

        public static int LastAnimateFrame = -1;

        public static Color Color = Color.White * 0.2f;

        public PrimitivesRenderer2D PrimitivesRenderer = new();

        public BoidsWidget() {
            IsDrawRequired = true;
            IsHitTestVisible = false;
        }

        public override void Draw(DrawContext dc) {
            if (Time.FrameIndex > LastAnimateFrame) {
                LastAnimateFrame = Time.FrameIndex;
                Point2 point = default;
                point.X = Window.Size.Y > 0 ? 9000 * Window.Size.X / Window.Size.Y : 9000;
                point.Y = 9000;
                if (Boids == null
                    || Grid == null
                    || Grid.GridSize != point) {
                    Boids = new DynamicArray<Boid>();
                    Grid = new BodyGrid(point, 250);
                    int num = 80;
                    for (int i = 0; i < num; i++) {
                        Boid item = new() {
                            Faction = i / 16,
                            Position = point / 2
                                + new Point2((int)(4000f * MathUtils.Sin(6.28f * i / num)), (int)(4000f * MathUtils.Cos(6.28f * i / num))),
                            Velocity = Random.Vector2(750f)
                        };
                        Boids.Add(item);
                    }
                }
                Grid.Clear();
                foreach (Boid boid in Boids) {
                    Grid.Add(boid);
                }
                foreach (Boid boid2 in Boids) {
                    boid2.Update();
                }
            }
            Vector2 vector = new(Grid.GridSize.X / 2, Grid.GridSize.Y / 2);
            float x = Window.Size.X / (float)Grid.GridSize.X;
            float x2 = Window.Size.Y / (float)Grid.GridSize.Y;
            float num2 = MathUtils.Min(x, x2);
            Matrix matrix = Matrix.CreateTranslation(0f - vector.X, 0f - vector.Y, 0f)
                * Matrix.CreateScale(num2, num2, 1f)
                * Matrix.CreateTranslation(Window.Size.X / 2f, Window.Size.Y / 2f, 0f);
            foreach (Boid boid3 in Boids) {
                boid3.Draw(PrimitivesRenderer);
            }
            PrimitivesRenderer.Flush(matrix * PrimitivesRenderer2D.ViewportMatrix());
        }
    }
}